/* myclient.cc: sample client program */

#include "connection.h"
#include "protocolexception.h"
#include "connectionclosedexception.h"
#include "messagehandler.h"

#include <iostream>
#include <algorithm>
#include "stdio.h"
#include <sstream>
#include <string>
#include <cstdlib>    /* for exit() and atoi() */

using namespace std;
using client_server::Connection;
using client_server::ConnectionClosedException;
using client_server::ProtocolException;

static client_server::MessageHandler mh;


unsigned int get_int(){
	stringstream s;
	unsigned int n;
	do {
		s.clear();
		string in;
		getline(cin, in);
		s.str(in);
		s >> n;
		if ( s.fail() ){
			cout << "Not a positive integer, try again... >" << in << "<" << endl;
		}
	} while (s.fail());
	//cout << "OK int: " << n << endl;
	return n;
}

string get_multi_string(){
	string s;
	getline(cin, s, static_cast<char>(4));
	//getline(cin, s, ',');
//	cout << "Multiline: >"  << s << "<" <<endl;
	cin.clear();
	return s;
}

string get_string(){
	string s;
	getline(cin, s);
//	cout << "Singleline: " << s << endl;
	return s;
}

void ln(Connection &conn) throw(ConnectionClosedException, ProtocolException){
		vector<pair<unsigned int, string> > v = mh.list_groups(&conn);
		if(v.size() == 0)
			cout  << endl << "No news groups found" << endl;
		else
			cout  << "Listing news groups as id:group_name" << endl;
		for(vector<pair<unsigned int, string> >::iterator it = v.begin();
			it != v.end(); it++){
				cout << it->first << ":" << it->second << endl;
			}

}

void la (Connection &conn) throw(ConnectionClosedException, ProtocolException){
	cout << "Enter group id: ";
	unsigned int n = get_int();
	vector<pair<unsigned int, string> > v = mh.list_art(&conn, n);
	if(v.size() == 1 && v.begin()->second == "-1" && v.begin()->first == 0){
		cout << "News group does not exist" << endl;
		return;
	}
	if(v.size() == 0){
		cout << "Group is empty" << endl;
		return;
	}
	cout << "id:group_name" << endl;
	for(vector<pair<unsigned int, string> >::iterator it = v.begin(); it != v.end(); it++){
		cout << it->first << ":" << it->second << endl;
	}
}

void rn(Connection &conn) throw(ConnectionClosedException, ProtocolException){
	cout << "Enter group id: ";
	unsigned int n = get_int();

	if(mh.delete_group(&conn, n))
		cout << "News group removed" << endl;
	else
		cout << "News group does not exist" << endl;
}

void ra(Connection &conn){
	cout << "Enter group id: ";
	unsigned int g = get_int();
	cout << "Enter article id: ";
	unsigned int a = get_int();
	int res = mh.delete_art(&conn, g, a);
	if(res == 0)
		cout << "Article removed" << endl;
	if(res == 1)
		cout << "News group not found" << endl;
	if(res == 2)
		cout << "Article not found" << endl;
}

void cn(Connection &conn) throw(ConnectionClosedException, ProtocolException){
	cout << "Enter a name for the new news group: ";
	string name = get_string();
	int status = mh.create_group(&conn, name);
	if(!status)
		cout << "News group \"" << name << "\" created" << endl;
	else	
		cout << "News group already exists" << endl;
}

void ca(Connection &conn) throw(ConnectionClosedException, ProtocolException){
	//Expecting num string string string
	cout << "Group ID (positive integer): ";
	unsigned int n = get_int();
	cout << "Title: ";
	string title = get_string();
	cout << "Author: ";
	string author = get_string();
	cout << "Text (terminate with ^d on an empty line): ";
	string text = get_multi_string();
	int status = mh.create_art(&conn, n, title, author, text);
	if(status == 0)
		cout << "Article \"" << title << "\" created" << endl;
	else if(status == -1)
		cout << "NG does not exist" << endl;
}

void ga(Connection &conn) throw(ConnectionClosedException, ProtocolException){
	cout << "Enter news group id: ";
	unsigned int group = get_int();
	cout << "Enter article id: ";
	unsigned int art = get_int();
	pair<int, map<string, string> > article = mh.get_art(&conn, group, art);
	if(article.first == 0){
	cout << "Title: " << article.second["TITLE"] << endl;
	cout << "Author: " << article.second["AUTHOR"] << endl;
	cout << "Text: " << article.second["TEXT"] << endl;
	} else if (article.first == 1) {
		cout << "News group does not exist" << endl;
	} else if (article.first == 2) {
		cout << "Article does not exist" << endl;
	}
	
}

void help(){    
	cout << "List newsgroups: ln" << endl;
	cout << "List article: la" << endl;
	cout << "Remove newsgroup: rn" << endl;
	cout << "Remove article: ra" << endl;
	cout << "Create newsgroup: cn" << endl;
	cout << "Create article: ca" << endl;
	cout << "Get article: ga" << endl;
	cout << "Quit: (q)uit" << endl;
}

int main(int argc, char* argv[]) {
    if (argc != 3) {
        cerr << "Usage: newsclient host-name port-number" << endl;
        exit(1);
    }
    
    Connection conn(argv[1], atoi(argv[2]));
    if (! conn.isConnected()) {
        cerr << "Connection attempt failed" << endl;
        exit(1);
    }
    
	cout << endl << ">> Type h for help"<<endl;
	while (true) { // 4 is the ascii value for end of transmission
		cout << endl << ">> ";
		string token;
	
	//cin.clear();
	//cout << "cin.fail() = "<<cin.fail() << endl;
	//cout << "cin.eof() = "<<cin.eof() << endl;
	//cout << "cin.good() = "<<cin.good() << endl;
	//cout << "cin.bad() = "<<cin.bad() << endl;
	//exit(1);
		getline(cin, token);
		try {
			if(token == "ln"){
				ln(conn);
			}
			else if(token == "la"){
				la(conn);
			} 
			else if(token == "rn"){
				rn(conn);
			}
			else if(token == "ra"){
				ra(conn);
			} 
			else if(token == "cn"){
				cn(conn);
			}
			else if(token == "ca"){
				ca(conn);
			}
			else if(token == "ga"){
				ga(conn);
			}
			else if(token == "h"){
				help();
			} 
			else if(token == "q" || token == "quit" || token == "(q)uit" /*teeheee*/){
				break;
			} else {
				cout << "<< " << token << " >> not a command" << endl;
			}

        } 
        catch (ConnectionClosedException&) {
            cerr << "Server closed down!" << endl;
            exit(1);
        }

		    catch(ProtocolException& e){
		        //delete conn;
		        cout << "<< Protocol breach >>" << endl << "function: " << e.fcn << endl <<
		        	"recieved: " << static_cast<int>(e.rec) << endl << "reference: " <<
		        	static_cast<int>(e.ref) << endl  << endl;
		    }
    }
}











